//
//  MXSon.m
//  MassageForward
//
//  Created by muxue on 2019/8/31.
//  Copyright © 2019 暮雪. All rights reserved.
//

#import "MXSon.h"
#import <objc/runtime.h>

@implementation MXSon


+ (MXSon *)sonWithName:(NSString *)name age:(NSInteger)age {
    MXSon *son = [[MXSon alloc] init];
    son.name = name;
    son.age = age;
    return son;
}
/**
 * 消息发送：runtime 通过 selector 快速查找 IMP 的过程，有了函数指针就可以找到对应的方法实现
 * 消息转发：runtime 通过 selector 查找 IMP 失败后执行一系列转发流程的慢速通道，如果不做处理，则会抛出异常
 */

/**
 * 动态添加的方法
 */
int dynamicMethodIMP(id self, SEL _cmd) {
    printf("SEL %s did not exist\n",sel_getName(_cmd));
    return 3;
}

/** 方法动态解析
 * 这一步如果没有给 sel 绑定方法，无论是返回 YES 还是 NO，都会进入下一步 forwardingTargetForSelector: 的调用
 * 这一步如果给该类添加了 sel 方法，无论返回 YES 还是 NO，都不会进入下一步 forwardingTargetForSelector: 的调用
 * 如果该 resolveInstanceMethod 方法不实现，系统会直接进入下一步 forwardingTargetForSelector: 的调用
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@">>>>---%s", __func__);
    if (sel == @selector(length)) {
        // 动态添加方法
        class_addMethod(self, sel, (IMP) dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

// ------------------------------- 方法重定向 ------------------------------
/**
 * 当 resolveInstanceMethod: 方法返回 NO 后，消息转发机制执行前，runtime 系统会给我们一次偷梁换柱的机会,runtime 系统会调用 forwardingTargetForSelector: 方法替换消息的接受者为其他对象
 * 当这一步的返回值为一个不为nil 的对象时，消息将转发给该对象
 * 当这一步的返回值为 nil 时，将进入下一步 methodSignatureForSelector: 生成一个方法签名
 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    if (aSelector == @selector(count)) {
        return @[@(1), @(1), @(3)];
    }
    return [super forwardingTargetForSelector:aSelector];
}

// --------------------------------- 正常转发 ----------------------------------
/** 其实在 forwardInvocation: 消息发送前，Runtime 系统会向对象发送 methodSignatureForSelector: 消息，并取到返回的方法签名用于生成 NSInvocation对象。所以我们在重写 forwardInvocation: 的同时也要重写 methodSignatureForSelector: 方法，否则会抛异常
 * 该方法如果返回 nil, 系统则会直接抛出异常，不在进行下一步 forwardInvocation: 方法的调用
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    // 不要影响其他方签名的调用
    NSMethodSignature *methodsignatrue = [super methodSignatureForSelector:aSelector];
    if (methodsignatrue) {
        return methodsignatrue;
    }
    
    // 生成一个固定的方法签名
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    
}

/**
 当一个对象由于没有相应的方法实现而无法响应某消息时，运行时系统将通过 forwardInvocation: 消息通知该对象。每个对象都从 NSObject 类中继承了 forwardInvocation: 方法。然而，NSObject 中的方法实现只是简单地调用了 doesNotRecognizeSelector:。通过实现我们自己的 forwardInvocation: 方法，我们可以在该方法实现中将消息转发给其它对象。
 * 这一步，实质上方法的调用对外界不做任何响应，相当于把消息给吞食掉
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s", __func__);
    if ([anInvocation.target respondsToSelector:anInvocation.selector]) {
        // 执行
        [anInvocation invoke];
    }
}

/** 系统默认为抛出异常 */
- (void)doesNotRecognizeSelector:(SEL)aSelector {
    
}

@end
